﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace LiJ.Orm.Core
{
    public class SqlParameterEmit
    {
        /// <summary>
        /// 获取类型下指定部分字段的委托方法
        /// </summary>
        /// <typeparam name="TMType">动态类型</typeparam>
        /// <param name="propertyInfoList">属性类型集合</param>
        /// <returns></returns>
        public static Func<object, Dictionary<string, object>> CreateDictionaryDeleMethod<TMType>(
            IEnumerable<PropertyInfo> propertyInfoList)
        {
            if (propertyInfoList == null)
                return null;

            var dictionaryType = typeof(Dictionary<string, object>);
            var dirAddMethod = dictionaryType.GetMethod("Add");
            var dictionaryConstructor = dictionaryType.GetConstructor(new[] {typeof(int)});

            var concatFuncMethod = new DynamicMethod(string.Concat("ConvertToFunc_", Guid.NewGuid().GetHashCode()),
                MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, dictionaryType,
                new[] {typeof(object)}, typeof(TMType), false);
            var ilGenerator = concatFuncMethod.GetILGenerator();
            ilGenerator.DeclareLocal(dictionaryType);

            ilGenerator.Emit(OpCodes.Nop);
            var propertyInfos = propertyInfoList.ToList();
            ilGenerator.Emit(OpCodes.Ldc_I4, propertyInfos.Count());
            ilGenerator.Emit(OpCodes.Newobj, dictionaryConstructor);

            ilGenerator.Emit(OpCodes.Stloc_0);

            foreach (var propertyInfo in propertyInfos)
            {
                ilGenerator.Emit(OpCodes.Nop);
                ilGenerator.Emit(OpCodes.Ldloc_0);

                ilGenerator.Emit(OpCodes.Ldstr, propertyInfo.Name);
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Callvirt, propertyInfo.GetGetMethod());

                if (propertyInfo.PropertyType.GetTypeInfo().IsValueType)
                {
                    if (propertyInfo.PropertyType == typeof(bool))
                    {
                        var boolJumpLabs = new[]
                        {
                            ilGenerator.DefineLabel(),
                            ilGenerator.DefineLabel()
                        };

                        ilGenerator.Emit(OpCodes.Brtrue_S, boolJumpLabs[0]);

                        ilGenerator.Emit(OpCodes.Ldc_I4_0);
                        ilGenerator.Emit(OpCodes.Br_S, boolJumpLabs[1]);

                        ilGenerator.MarkLabel(boolJumpLabs[0]);
                        ilGenerator.Emit(OpCodes.Ldc_I4_1);
                        ilGenerator.MarkLabel(boolJumpLabs[1]);
                        ilGenerator.Emit(OpCodes.Box, typeof(int));
                    }
                    else
                    {
                        ilGenerator.Emit(OpCodes.Box, propertyInfo.PropertyType);
                    }
                }
                ilGenerator.Emit(OpCodes.Callvirt, dirAddMethod);
            }
            ilGenerator.Emit(OpCodes.Nop);
            ilGenerator.Emit(OpCodes.Ldloc_0);
            ilGenerator.Emit(OpCodes.Ret);

            return concatFuncMethod.CreateDelegate(typeof(Func<object, Dictionary<string, object>>)) as
                Func<object, Dictionary<string, object>>;
        }
    }
}